using System.Collections.Generic;
using log4net.Core;
using QuickGraph;
using Quiki.ExpressionParsers;
using Wintellect.PowerCollections;

namespace Quiki.DocumentGraphProcessors
{
    public class LinkGraphConstructor : IDocumentGraphProcessor
    {
        public LinkGraphConstructor(ILogger logger)
        {
            this.logger = logger;
        }

        public void Execute(DocumentGraph documentGraph)
        {
            foreach (WikiDocument document in documentGraph.Documents.Values)
                linkGraph.AddVertex (document.Reference.ToString());

            foreach (WikiDocument document in documentGraph.Documents.Values)
                ProcessDocument (document, documentGraph);
        }

        private void ProcessDocument (WikiDocument document, DocumentGraph documentGraph)
        {
            logger.PushContext(document.FileName);
            ProcessElement (document.RootElement, document, documentGraph);
            logger.PopContext();
        }

        private void ProcessElement (WikiTextElement element, WikiDocument document, DocumentGraph documentGraph)
        {
            if (element.ElementType == WikiTextElementType.InternalLink)
                CheckInternalLink(element, document, documentGraph);

            foreach (WikiTextElement childElement in element.ChildElements)
                ProcessElement (childElement, document, documentGraph);
        }

        private void CheckInternalLink(WikiTextElement element, WikiDocument document, DocumentGraph documentGraph)
        {
            WikiInternalLinkElement internalLinkElement = (WikiInternalLinkElement) element;
            WikiDocReference docReference = internalLinkElement.Reference;

            if (docReference.HasExplicitNamespace && docReference.IsCategory)
                categories.Add(internalLinkElement.Reference.ToString(), document);
            else
            {
                string source = document.Reference.ToString ();
                string dest = docReference.ToString ();

                if (false == linkGraph.ContainsVertex (source))
                    throw new KeyNotFoundException (source);

                if (false == linkGraph.ContainsVertex (dest))
                    logger.Log (Level.Warn, "Linked page {0} does not exist", dest);
                else
                {
                    linkGraph.AddEdge(new Edge<string>(source, dest));
                    internalLinkElement.Exists = true;
                    
                    if (string.IsNullOrEmpty(internalLinkElement.LinkText))
                    {
                        WikiDocument referencedDoc = documentGraph.FindDocument(docReference);
                        internalLinkElement.LinkText = referencedDoc.Title;
                    }
                }
            }
        }

        private readonly BidirectionalGraph<string, Edge<string>> linkGraph
            = new BidirectionalGraph<string, Edge<string>> ();

        private MultiDictionary<string, WikiDocument> categories = new MultiDictionary<string, WikiDocument>(false);
        private readonly ILogger logger;
    }
}